# Copyright (c) 2023, Oracle and/or its affiliates.
#
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
#

import pytest
from typing import Dict, Callable, List, Any
from logging import getLogger, Logger
import copy
from .controller.innodbcluster.logs.logs_types_api import GeneralLogSpec, ErrorLogSpec, SlowQueryLogSpec
from .controller.innodbcluster.logs.logs_api import LogsSpec, LogCollectorSpec, ServerLogType
from .controller.innodbcluster.logs.logs_collector_fluentd_api import FluentdSpec, FluentdMysqlLogSpec, FluentdRecordAugmentationSpec
from .controller.api_utils import ApiSpecError


@pytest.fixture
def logger() -> Logger:
    return getLogger()

@pytest.fixture
def general_log_factory() -> Callable[[], GeneralLogSpec]:
    return lambda : GeneralLogSpec()

@pytest.fixture
def general_log_spec_enabled_noncollected() -> dict:
    return {
        "enabled" : True,
        "collect" : False,
    }

@pytest.fixture
def general_log_spec_nonenabled_noncollected() -> Dict:
    return {
        "enabled" : False,
        "collect" : False,
    }

@pytest.fixture
def general_log_spec_enabled_collected() -> Dict:
    return {
        "enabled" : True,
        "collect" : True,
    }

@pytest.fixture
def general_log_spec_nonenabled_collected() -> Dict:
    return {
        "enabled" : False,
        "collect" : True,
    }

def test_general_log_spec(general_log_factory: Callable[[], GeneralLogSpec],
                          general_log_spec_enabled_noncollected: Dict,
                          general_log_spec_nonenabled_noncollected: Dict,
                          general_log_spec_enabled_collected: Dict,
                          general_log_spec_nonenabled_collected: Dict,
                          logger: Logger) -> None:
    prefix = ServerLogType.GENERAL.value
    test_obj = general_log_factory()

    assert test_obj.enabled == None
    assert test_obj.collect == False

    fixture = general_log_spec_enabled_noncollected
    test_obj = general_log_factory()
    test_obj.parse(fixture, prefix, logger)
    assert test_obj.enabled == fixture["enabled"]
    assert test_obj.collect == fixture["collect"]
    test_obj.validate()
    cm = test_obj.get_cm_data(logger)
    print(cm)
    assert cm == {
        "general-log.cnf" : f"""# Generated by MySQL Operator for Kubernetes
[mysqld]
general_log=1
general_log_file={test_obj.fileName}"""
    }
    sts = {"spec": {"template": {"spec" : { "containers": []}}}}
    container_name = "mysql"
    cm_name = "ourcluster-logs-config"
    test_obj.add_to_sts_spec(sts, container_name, cm_name, logger)
    assert sts == { "spec":{ "template":{ "spec": {
        "containers":[
                {
                    "name":"mysql",
                    "volumeMounts":[
                        {
                            "mountPath":"/etc/my.cnf.d/general-log.cnf",
                            "name":"general-log-config",
                            "subPath":"general-log.cnf"
                        }
                    ]
                }
            ],
        "volumes":[
                {
                    "configMap":{
                        "defaultMode":420,
                        "items":[
                            {
                                "key":"general-log.cnf",
                                "path":"general-log.cnf"
                            }
                        ],
                        "name":"ourcluster-logs-config"
                    },
                    "name":"general-log-config"
                }
        ]
    }}}}


    fixture = general_log_spec_nonenabled_noncollected
    test_obj = general_log_factory()
    test_obj.parse(fixture, prefix, logger)
    assert test_obj.enabled == fixture["enabled"]
    assert test_obj.collect == fixture["collect"]
    test_obj.validate()
    cm = test_obj.get_cm_data(logger)
    print(cm)
    assert cm == {
        "general-log.cnf" : """# Generated by MySQL Operator for Kubernetes
[mysqld]
general_log=0"""
    }
    sts = {"spec": {"template": {"spec" : { "containers": []}}}}
    container_name = "mysql"
    cm_name = "ourcluster-logs-config"
    test_obj.add_to_sts_spec(sts, container_name, cm_name, logger)
    assert sts == {"spec": { "template": { "spec": {
            "containers":[
               {
                  "name":"mysql",
                  "volumeMounts":[
                     {
                        "mountPath":"/etc/my.cnf.d/general-log.cnf",
                        "name":"general-log-config",
                        "subPath":"general-log.cnf"
                     }
                  ]
               }
            ],
            "volumes":[
               {
                  "configMap":{
                     "defaultMode":420,
                     "items":[
                        {
                           "key":"general-log.cnf",
                           "path":"general-log.cnf"
                        }
                     ],
                     "name":"ourcluster-logs-config"
                  },
                  "name":"general-log-config"
               }
            ]
    }}}}

    fixture = general_log_spec_enabled_collected
    test_obj = general_log_factory()
    test_obj.parse(fixture, prefix, logger)
    assert test_obj.enabled == fixture["enabled"]
    assert test_obj.collect == fixture["collect"]
    test_obj.validate()
    cm = test_obj.get_cm_data(logger)
    print(cm)
    assert cm == {
        "general-log.cnf" : f"""# Generated by MySQL Operator for Kubernetes
[mysqld]
general_log=1
general_log_file={test_obj.fileName}"""
    }
    sts = {"spec": {"template": {"spec" : { "containers": []}}}}
    container_name = "mysql"
    cm_name = "ourcluster-logs-config"
    test_obj.add_to_sts_spec(sts, container_name, cm_name, logger)
    assert sts == {"spec": { "template": { "spec": {
            "containers":[
               {
                  "name":"mysql",
                  "volumeMounts":[
                     {
                        "mountPath":"/etc/my.cnf.d/general-log.cnf",
                        "name":"general-log-config",
                        "subPath":"general-log.cnf"
                     }
                  ]
               }
            ],
            "volumes":[
               {
                  "configMap":{
                     "defaultMode":420,
                     "items":[
                        {
                           "key":"general-log.cnf",
                           "path":"general-log.cnf"
                        }
                     ],
                     "name":"ourcluster-logs-config"
                  },
                  "name":"general-log-config"
               }
            ]
    }}}}

    fixture = general_log_spec_nonenabled_collected
    test_obj = general_log_factory()
    test_obj.parse(fixture, prefix, logger)
    assert test_obj.enabled == fixture["enabled"]
    assert test_obj.collect == fixture["collect"]
    with pytest.raises(ApiSpecError, match=rf"{prefix}.collect is enabled while {prefix}.enabled is not$") as e_info:
        test_obj.validate()


@pytest.fixture
def slow_query_log_factory() -> Callable[[], SlowQueryLogSpec]:
    return lambda : SlowQueryLogSpec()

@pytest.fixture
def slow_query_log_spec_enabled_noncollected() -> Dict:
    return {
        "enabled" : True,
        "collect" : False,
    }

@pytest.fixture
def slow_query_log_spec_nonenabled_noncollected() -> Dict:
    return {
        "enabled" : False,
        "collect" : False,
    }

@pytest.fixture
def slow_query_log_spec_enabled_collected() -> Dict:
    return {
        "enabled" : True,
        "collect" : True,
        "longQueryTime" : 2.5,
    }

@pytest.fixture
def slow_query_log_spec_nonenabled_collected() -> Dict:
    return {
        "enabled" : False,
        "collect" : True,
    }

def test_slow_query_log_spec(slow_query_log_factory: Callable[[], SlowQueryLogSpec],
                             slow_query_log_spec_enabled_noncollected: Dict,
                             slow_query_log_spec_nonenabled_noncollected: Dict,
                             slow_query_log_spec_enabled_collected: Dict,
                             slow_query_log_spec_nonenabled_collected: Dict,
                             logger: Logger) -> None:
    prefix = ServerLogType.SLOW_QUERY.value
    test_obj = slow_query_log_factory()

    assert test_obj.enabled == None
    assert test_obj.collect == False

    fixture = slow_query_log_spec_enabled_noncollected
    test_obj = slow_query_log_factory()
    test_obj.parse(fixture, prefix, logger)
    assert test_obj.enabled == fixture["enabled"]
    assert test_obj.collect == fixture["collect"]
    test_obj.validate()
    cm = test_obj.get_cm_data(logger)
    print(cm)
    assert cm == {
        "slow-query-log.cnf" : f"""# Generated by MySQL Operator for Kubernetes
[mysqld]
slow_query_log=1
slow_query_log_file='{test_obj.fileName}'
log_slow_admin_statements=1"""
    }
    sts = {"spec": {"template": {"spec" : { "containers": []}}}}
    container_name = "mysql"
    cm_name = "ourcluster-logs-config"
    test_obj.add_to_sts_spec(sts, container_name, cm_name, logger)
    assert sts == {"spec": { "template": { "spec": {
            "containers":[
               {
                  "name":"mysql",
                  "volumeMounts":[
                     {
                        "mountPath":"/etc/my.cnf.d/slow-query-log.cnf",
                        "name":"slow-query-log-config",
                        "subPath":"slow-query-log.cnf"
                     }
                  ]
               }
            ],
            "volumes":[
               {
                  "configMap":{
                     "defaultMode":420,
                     "items":[
                        {
                           "key":"slow-query-log.cnf",
                           "path":"slow-query-log.cnf"
                        }
                     ],
                     "name":"ourcluster-logs-config"
                  },
                  "name":"slow-query-log-config"
               }
            ]
    }}}}

    fixture = slow_query_log_spec_nonenabled_noncollected
    test_obj = slow_query_log_factory()
    test_obj.parse(fixture, prefix, logger)
    assert test_obj.enabled == fixture["enabled"]
    assert test_obj.collect == fixture["collect"]
    test_obj.validate()
    cm = test_obj.get_cm_data(logger)
    print(cm)
    assert cm == {
        "slow-query-log.cnf" : f"""# Generated by MySQL Operator for Kubernetes
[mysqld]
slow_query_log=0"""
    }
    sts = {"spec": {"template": {"spec" : { "containers": []}}}}
    container_name = "mysql"
    cm_name = "ourcluster-logs-config"
    test_obj.add_to_sts_spec(sts, container_name, cm_name, logger)
    assert sts == {"spec": { "template": { "spec": {
            "containers":[
               {
                  "name":"mysql",
                  "volumeMounts":[
                     {
                        "mountPath":"/etc/my.cnf.d/slow-query-log.cnf",
                        "name":"slow-query-log-config",
                        "subPath":"slow-query-log.cnf"
                     }
                  ]
               }
            ],
            "volumes":[
               {
                  "configMap":{
                     "defaultMode":420,
                     "items":[
                        {
                           "key":"slow-query-log.cnf",
                           "path":"slow-query-log.cnf"
                        }
                     ],
                     "name":"ourcluster-logs-config"
                  },
                  "name":"slow-query-log-config"
               }
            ]
    }}}}

    fixture = slow_query_log_spec_enabled_collected
    test_obj = slow_query_log_factory()
    test_obj.parse(fixture, prefix, logger)
    assert test_obj.enabled == fixture["enabled"]
    assert test_obj.collect == fixture["collect"]
    test_obj.validate()

    cm = test_obj.get_cm_data(logger)
    print(cm)
    assert cm == {
        "slow-query-log.cnf" : f"""# Generated by MySQL Operator for Kubernetes
[mysqld]
slow_query_log=1
slow_query_log_file='{test_obj.fileName}'
log_slow_admin_statements=1
long_query_time=2.5"""
    }
    sts = {"spec": {"template": {"spec" : { "containers": []}}}}
    container_name = "mysql"
    cm_name = "ourcluster-logs-config"
    test_obj.add_to_sts_spec(sts, container_name, cm_name, logger)
    assert sts == {"spec": { "template": { "spec": {
            "containers":[
               {
                  "name":"mysql",
                  "volumeMounts":[
                     {
                        "mountPath":"/etc/my.cnf.d/slow-query-log.cnf",
                        "name":"slow-query-log-config",
                        "subPath":"slow-query-log.cnf"
                     }
                  ]
               }
            ],
            "volumes":[
               {
                  "configMap":{
                     "defaultMode":420,
                     "items":[
                        {
                           "key":"slow-query-log.cnf",
                           "path":"slow-query-log.cnf"
                        }
                     ],
                     "name":"ourcluster-logs-config"
                  },
                  "name":"slow-query-log-config"
               }
            ]
    }}}}

    fixture = slow_query_log_spec_nonenabled_collected
    test_obj = slow_query_log_factory()
    test_obj.parse(fixture, prefix, logger)
    assert test_obj.enabled == fixture["enabled"]
    assert test_obj.collect == fixture["collect"]
    with pytest.raises(ApiSpecError, match=rf"{prefix}.collect is enabled while {prefix}.enabled is not$") as e_info:
        test_obj.validate()


@pytest.fixture
def error_log_factory() -> Callable[[], ErrorLogSpec]:
    return lambda : ErrorLogSpec()

@pytest.fixture
def error_log_spec_collected() -> Dict:
    return {
        "collect" : True,
    }

@pytest.fixture
def error_log_spec_noncollected() -> Dict:
    return {
        "collect" : False,
    }


def test_error_log_spec(error_log_factory: Callable[[], ErrorLogSpec],
                        error_log_spec_collected: Dict,
                        error_log_spec_noncollected: Dict,
                        logger: Logger) -> None:
    prefix = ServerLogType.ERROR.value
    test_obj = error_log_factory()
    assert test_obj.collect == False

    fixture = error_log_spec_collected
    test_obj = error_log_factory()
    test_obj.parse(fixture, prefix, logger)
    assert test_obj.collect == fixture["collect"]
    test_obj.validate()
    cm = test_obj.get_cm_data(logger)
    print(cm)
    assert cm == {
        "error-log.cnf" : f"""# Generated by MySQL Operator for Kubernetes
[mysqld]
log_error_verbosity=3
log_error='{(test_obj.error_log_name)}'
log_error_services='log_sink_json'"""
    }
    sts = {"spec": {"template": {"spec" : { "containers": []}}}}
    container_name = "mysql"
    cm_name = "ourcluster-logs-config"
    test_obj.add_to_sts_spec(sts, container_name, cm_name, logger)
    assert sts == {"spec": { "template": { "spec": {
            "containers":[
               {
                  "name":"mysql",
                  "volumeMounts":[
                     {
                        "mountPath":"/etc/my.cnf.d/error-log.cnf",
                        "name":"error-log-config",
                        "subPath":"error-log.cnf"
                     }
                  ]
               }
            ],
            "volumes":[
               {
                  "configMap":{
                     "defaultMode":420,
                     "items":[
                        {
                           "key":"error-log.cnf",
                           "path":"error-log.cnf"
                        }
                     ],
                     "name":"ourcluster-logs-config"
                  },
                  "name":"error-log-config"
               }
            ]
    }}}}

    fixture = error_log_spec_noncollected
    test_obj = error_log_factory()
    test_obj.parse(fixture, prefix, logger)
    assert test_obj.collect == fixture["collect"]
    test_obj.validate()
    sts = {"spec": {"template": {"spec" : { "containers": []}}}}
    container_name = "mysql"
    cm_name = "ourcluster-logs-config"
    test_obj.add_to_sts_spec(sts, container_name, cm_name, logger)
    assert sts == {"spec": { "template": { "spec": {
            "containers":[
               {
                  "name":"mysql",
                  "volumeMounts":[
                     {
                        "mountPath":"/etc/my.cnf.d/error-log.cnf",
                        "name":"error-log-config",
                        "subPath":"error-log.cnf"
                     }
                  ]
               }
            ],
            "volumes":[
               {
                  "configMap":{
                     "defaultMode":420,
                     "items":[
                        {
                           "key":"error-log.cnf",
                           "path":"error-log.cnf"
                        }
                     ],
                     "name":"ourcluster-logs-config"
                  },
                  "name":"error-log-config"
               }
            ]
    }}}}


@pytest.fixture
def logs_spec1(general_log_spec_enabled_noncollected, slow_query_log_spec_enabled_noncollected, error_log_spec_collected) -> Dict:
    return {
        ServerLogType.GENERAL.value : general_log_spec_enabled_noncollected,
        ServerLogType.SLOW_QUERY.value : slow_query_log_spec_enabled_noncollected,
        ServerLogType.ERROR.value : error_log_spec_collected,
        "collector": {
            "image": "somerepo/someimage",
        }
    }

@pytest.fixture
def logs_spec2(general_log_spec_nonenabled_noncollected, slow_query_log_spec_nonenabled_noncollected, error_log_spec_noncollected) -> Dict:
    return {
        ServerLogType.GENERAL.value : general_log_spec_nonenabled_noncollected,
        ServerLogType.SLOW_QUERY.value : slow_query_log_spec_nonenabled_noncollected,
        ServerLogType.ERROR.value : error_log_spec_noncollected,
    }

@pytest.fixture
def logs_spec3(general_log_spec_enabled_collected, slow_query_log_spec_enabled_collected, error_log_spec_collected) -> Dict:
    return {
        ServerLogType.GENERAL.value : general_log_spec_enabled_collected,
        ServerLogType.SLOW_QUERY.value : slow_query_log_spec_enabled_collected,
        ServerLogType.ERROR.value : error_log_spec_collected,
        "collector": {
            "image": "somerepo/someimage",
            "fluentd": {
                "sinks": [
                    {
                        "name": "stdout",
                        "rawConfig": """
<store>
    @type stdout
</store>
"""
                    }
                ]
            }
        }
    }

@pytest.fixture
def logs_spec4(general_log_spec_nonenabled_collected, slow_query_log_spec_enabled_collected, error_log_spec_noncollected) -> dict:
    return {
        ServerLogType.GENERAL.value : general_log_spec_nonenabled_collected,
        ServerLogType.SLOW_QUERY.value : slow_query_log_spec_enabled_collected,
        ServerLogType.ERROR.value : error_log_spec_noncollected,
        "collector": {
            "image": "somerepo/someimage",
            "fluentd": {
                "sinks": [
                    {
                        "name": "stdout",
                        "rawConfig": """
<store>
    @type stdout
</store>
"""
                    }
                ]
            }
        }
    }

@pytest.fixture
def logs_spec5(general_log_spec_enabled_collected, slow_query_log_spec_nonenabled_collected, error_log_spec_noncollected) -> dict:
    return {
        ServerLogType.GENERAL.value : general_log_spec_enabled_collected,
        ServerLogType.SLOW_QUERY.value : slow_query_log_spec_nonenabled_collected,
        ServerLogType.ERROR.value : error_log_spec_noncollected,
        "collector": {
            "image": "somerepo/someimage",
            "fluentd": {
                "sinks": [
                    {
                        "name": "stdout",
                        "rawConfig": """
<store>
    @type stdout
</store>
"""
                    }
                ]
            }
        }
    }

@pytest.fixture
def logs_spec4_configmaps() -> List[Dict[str, Any]]:
    return [
   {
      "apiVersion":"v1",
      "kind":"ConfigMap",
      "metadata":{
         "name":"mycluster-logs-config"
      },
      "data":{
         "general-log.cnf":"# Generated by MySQL Operator for Kubernetes\n"
                           "[mysqld]\n"
                           "general_log=1\n"
                           "general_log_file=general_query.log",
         "error-log.cnf":"# Generated by MySQL Operator for Kubernetes\n"
                         "[mysqld]\n"
                         "log_error_verbosity=3\n"
                         "log_error='error.log'\n"
                         "log_error_services='log_sink_json'",
         "slow-query-log.cnf":"# Generated by MySQL Operator for Kubernetes\n"
                              "[mysqld]\nslow_query_log=1\n"
                              "slow_query_log_file='slow_query.log'\n"
                              "log_slow_admin_statements=1\n"
                              "long_query_time=2.5"
      }
   },
   {
      "apiVersion":"v1",
      "kind":"ConfigMap",
      "metadata":{
         "name":"mycluster-fluentd-conf"
      },
      "data":{
         "fluent.conf":"###### Generated by the MySQL Operator for Kubernetes ######\n\n"
                       "#GENERAL QUERY LOG SOURCE\n"
                       "<source>\n"
                       "  @type tail\n"
                       "  refresh_interval 5\n"
                       "  path  /var/lib/mysql/general_query.log\n"
                       "  read_from_head true\n"
                       "  pos_file /tmp/fluent/general_query.log.pos\n"
                       "  multiline_flush_interval 5s\n"
                       "  tag general_log\n"
                       "\n"
                       "  \n"
                       "  <parse>\n"
                       "    @type multiline\n"
                       "    # 2023-02-02T10:01:54.836730Z         6 Prepare   CREATE TABLE IF NOT EXISTS slave_worker_info\n"
                       "    format_firstline /^\\d{4}-\\d{1,2}-\\d{1,2}.*/\n"
                       "    format1 /^(?<time>\\d{4}-\\d{1,2}-\\d{1,2}\\S+)\\s+(?<thread>\\d+)\\s+(?<command_type>\\w+)\\s+(?<command>.*)\\s{0,}/\n"
                       "    time_key time\n"
                       "    time_format %iso8601\n"
                       "  </parse>\n</source>\n\n\n\n"
                       "#SLOW QUERY LOG SOURCE\n"
                       "<source>\n"
                       "  @type tail\n"
                       "  refresh_interval 5\n"
                       "  path /var/lib/mysql/slow_query.log\n"
                       "  read_from_head true\n"
                       "  pos_file /tmp/fluent/slow_query.log.pos\n"
                       "  multiline_flush_interval 5s\n"
                       "  tag slow_query_log\n"
                       "  emit_unmatched_lines false\n"
                       "  #  enable_watch_timer true\n"
                       "  \n"
                       "  <parse>\n"
                       "    @type multiline\n"
                       "    # \"# Time: 2023-01-31T14:14:44.570056Z\\n# User@Host: root[root] @ localhost []  Id:   185\\n# Query_time: 5.001852  Lock_time: 0.000000 Rows_sent: 1  Rows_examined: 1\\nSET timestamp=1675174479;\\nselect sleep(5);\n"
                       "    format_firstline /^#\\s+Time:/\n"
                       "    format1 /#\\s+Time:\\s+(?<time>\\S+).*/\n"
                       "    format2 /#\\s+User@Host:\\s+(?<user>[^\\s]*)\\[(?<current_user>[^\\s]*)\\]\\s+@\\s+(?<host>[^\\s]*)\\s+\\[(?<ip>[\\d\\.]{0,})\\]\\s+Id:\\s+(?<id>\\d+)\\s+/\n"
                       "    format3 /#\\s+Query_time:\\s+(?<query_time>\\d+\\.\\d+)\\s+Lock_time:\\s+(?<lock_time>\\d+\\.\\d+)\\s+Rows_sent:\\s+(?<rows_sent>\\d+)\\s+Rows_examined:\\s+(?<rows_examined>\\d+)\\s+/\n"
                       "    format4 /(((use\\s(?<schema>\\S+))|(SET\\s+timestamp=(?<timestamp>\\d+))|(?<query>.*));\\s+)+/\n"
                       "    format5 /(?<query>.*)/\n"
                       "    types id:integer,query_time:float,lock_time:float,rows_sent:integer,rows_examined:integer,timestamp:integer\n"
                       "    time_key time\n"
                       "    time_format %iso8601\n"
                       "  </parse>\n</source>\n\n\n\n"
                       "#ERROR LOG SOURCE\n"
                       "<source>\n"
                       "  @type tail\n"
                       "  path /var/lib/mysql/error.log.*.json\n"
                       "  refresh_interval 5\n"
                       "  read_from_head true\n"
                       "  pos_file /tmp/fluent/error.log.pos\n"
                       "  tag error_log\n"
                       "  <parse>\n"
                       "    @type json\n"
                       "    time_key time\n"
                       "    time_format %iso8601\n"
                       "  </parse>\n"
                       "</source>\n\n"
                       "<match *.**>\n"
                       "  @type copy\n"
                       "  \n"
                       "  <store>\n"
                       "      @type stdout\n"
                       "  </store>\n"
                       "  \n"
                       "</match>"
      }
   }
]


@pytest.fixture
def logs_spec_factory() -> Callable[[], LogsSpec]:
    return lambda : LogsSpec("myns", "mycluster")

def test_logs_spec(logs_spec_factory: Callable[[], LogsSpec],
                   logs_spec1: Dict,
                   logs_spec2: Dict,
                   logs_spec3: Dict,
                   logs_spec4: Dict,
                   logs_spec5: Dict,
                   logs_spec4_configmaps: List,
                   logger: Logger) -> None:
    prefix = "logs"

    fixture = logs_spec1
    test_obj = logs_spec_factory()
    test_obj.parse(fixture, prefix, logger)
    with pytest.raises(ApiSpecError, match=f"No collector configured") as e_info:
        test_obj.validate()
    assert test_obj.collect == True
    assert isinstance(test_obj.collector, LogCollectorSpec)
    get_configmaps_cb = test_obj.get_configmaps_cb()
    cm_prefix = '42'
    cm = get_configmaps_cb(cm_prefix, logger)
    sts = {"spec": {"template" : { "spec": {}}}}
    add_to_sts = test_obj.get_add_to_sts_cb()
    with pytest.raises(ApiSpecError, match=f"No collector configured") as e_info:
        add_to_sts(sts, logger)

    fixture = logs_spec2
    test_obj = logs_spec_factory()
    test_obj.parse(fixture, prefix, logger)
    test_obj.validate()
    assert test_obj.collect == False
    assert isinstance(test_obj.collector, LogCollectorSpec)
    get_configmaps_cb = test_obj.get_configmaps_cb()
    assert get_configmaps_cb is not None
    add_to_sts = test_obj.get_add_to_sts_cb()
    assert add_to_sts is not None

    fixture = logs_spec3
    test_obj = logs_spec_factory()
    test_obj.parse(fixture, prefix, logger)
    test_obj.validate()
    assert test_obj.collect == True
    assert isinstance(test_obj.collector, LogCollectorSpec)
    assert isinstance(test_obj.collector.collector, FluentdSpec)
    get_configmaps_cb = test_obj.get_configmaps_cb()
    cm_prefix = '42'
    cm = get_configmaps_cb(cm_prefix, logger)
    print(cm)
    assert cm == [
        {
            "apiVersion": "v1",
            "kind": "ConfigMap",
            "metadata": {
                'name': 'mycluster-logs-config'
            },
            "data": {
                f"{cm_prefix}error-log.cnf": "# Generated by MySQL Operator for Kubernetes\n"
                                    "[mysqld]\n"
                                    "log_error_verbosity=3\n"
                                    "log_error='error.log'\n"
                                    "log_error_services='log_sink_json'",
                f"{cm_prefix}general-log.cnf": "# Generated by MySQL Operator for Kubernetes\n"
                                    "[mysqld]\n"
                                    "general_log=1\n"
                                    "general_log_file=general_query.log",
                f"{cm_prefix}slow-query-log.cnf": "# Generated by MySQL Operator for Kubernetes\n"
                                        "[mysqld]\n"
                                        "slow_query_log=1\n"
                                        "slow_query_log_file='slow_query.log'\n"
                                        "log_slow_admin_statements=1\n"
                                        "long_query_time=2.5"
            },
        },
        {
            "apiVersion": "v1",
            "kind": "ConfigMap",
            "metadata": {
                'name': 'mycluster-fluentd-conf'
            },
            "data": {
                'fluent.conf':  '###### Generated by the MySQL Operator for '
                                'Kubernetes ######\n'
                                '\n'
                                '#GENERAL QUERY LOG SOURCE\n'
                                '<source>\n'
                                '  @type tail\n'
                                '  refresh_interval 5\n'
                                '  path  /var/lib/mysql/general_query.log\n'
                                '  read_from_head true\n'
                                '  pos_file /tmp/fluent/general_query.log.pos\n'
                                '  multiline_flush_interval 5s\n'
                                '  tag general_log\n'
                                '\n'
                                '  \n'
                                '  <parse>\n'
                                '    @type multiline\n'
                                '    # 2023-02-02T10:01:54.836730Z         6 '
                                'Prepare   CREATE TABLE IF NOT EXISTS '
                                'slave_worker_info\n'
                                '    format_firstline /^\\d{4}-\\d{1,2}-\\d{1,2}.*/\n'
                                '    format1 '
                                '/^(?<time>\\d{4}-\\d{1,2}-\\d{1,2}\\S+)\\s+(?<thread>\\d+)\\s+(?<command_type>\\w+)\\s+(?<command>.*)\\s{0,}/\n'
                                '    time_key time\n'
                                '    time_format %iso8601\n'
                                '  </parse>\n'
                                '</source>\n'
                                '\n'
                                '\n'
                                '\n'
                                '#SLOW QUERY LOG SOURCE\n'
                                '<source>\n'
                                '  @type tail\n'
                                '  refresh_interval 5\n'
                                '  path /var/lib/mysql/slow_query.log\n'
                                '  read_from_head true\n'
                                '  pos_file /tmp/fluent/slow_query.log.pos\n'
                                '  multiline_flush_interval 5s\n'
                                '  tag slow_query_log\n'
                                '  emit_unmatched_lines false\n'
                                '  #  enable_watch_timer true\n'
                                '  \n'
                                '  <parse>\n'
                                '    @type multiline\n'
                                '    # "# Time: 2023-01-31T14:14:44.570056Z\\n# '
                                'User@Host: root[root] @ localhost []  Id:   185\\n# '
                                'Query_time: 5.001852  Lock_time: 0.000000 '
                                'Rows_sent: 1  Rows_examined: 1\\nSET '
                                'timestamp=1675174479;\\nselect sleep(5);\n'
                                '    format_firstline /^#\\s+Time:/\n'
                                '    format1 /#\\s+Time:\\s+(?<time>\\S+).*/\n'
                                '    format2 '
                                '/#\\s+User@Host:\\s+(?<user>[^\\s]*)\\[(?<current_user>[^\\s]*)\\]\\s+@\\s+(?<host>[^\\s]*)\\s+\\[(?<ip>[\\d\\.]{0,})\\]\\s+Id:\\s+(?<id>\\d+)\\s+/\n'
                                '    format3 '
                                '/#\\s+Query_time:\\s+(?<query_time>\\d+\\.\\d+)\\s+Lock_time:\\s+(?<lock_time>\\d+\\.\\d+)\\s+Rows_sent:\\s+(?<rows_sent>\\d+)\\s+Rows_examined:\\s+(?<rows_examined>\\d+)\\s+/\n'
                                '    format4 '
                                '/(((use\\s(?<schema>\\S+))|(SET\\s+timestamp=(?<timestamp>\\d+))|(?<query>.*));\\s+)+/\n'
                                '    format5 /(?<query>.*)/\n'
                                '    types '
                                'id:integer,query_time:float,lock_time:float,rows_sent:integer,rows_examined:integer,timestamp:integer\n'
                                '    time_key time\n'
                                '    time_format %iso8601\n'
                                '  </parse>\n'
                                '</source>\n'
                                '\n'
                                '\n'
                                '\n'
                                '#ERROR LOG SOURCE\n'
                                '<source>\n'
                                '  @type tail\n'
                                '  path /var/lib/mysql/error.log.*.json\n'
                                '  refresh_interval 5\n'
                                '  read_from_head true\n'
                                '  pos_file /tmp/fluent/error.log.pos\n'
                                '  tag error_log\n'
                                '  <parse>\n'
                                '    @type json\n'
                                '    time_key time\n'
                                '    time_format %iso8601\n'
                                '  </parse>\n'
                                '</source>\n'
                                '\n'
                                '<match *.**>\n'
                                '  @type copy\n'
                                '  \n'
                                '  <store>\n'
                                '      @type stdout\n'
                                '  </store>\n'
                                '  \n'
                                '</match>'
            }
        }
    ]
    sts = {"spec": {"template" : { "spec": {}}}}
    add_to_sts = test_obj.get_add_to_sts_cb()
    add_to_sts(sts, logger)
    assert sts == {
        "spec":{
            "template":{
                "spec":{
                    "containers":[
                        {
                            "name":"mysql",
                            "volumeMounts":[
                                {
                                    "name":"general-log-config",
                                    "mountPath":"/etc/my.cnf.d/general-log.cnf",
                                    "subPath":"general-log.cnf"
                                },
                                {
                                    "name":"error-log-config",
                                    "mountPath":"/etc/my.cnf.d/error-log.cnf",
                                    "subPath":"error-log.cnf"
                                },
                                {
                                    "name":"slow-query-log-config",
                                    "mountPath":"/etc/my.cnf.d/slow-query-log.cnf",
                                    "subPath":"slow-query-log.cnf"
                                }
                            ]
                        },
                        {
                            "name":"logcollector",
                            "image":"somerepo/someimage",
                            "securityContext":{
                                "readOnlyRootFilesystem": False
                            },
                            "env": None,
                            "volumeMounts":[
                                {
                                    "name":"datadir",
                                    "mountPath":"/var/lib/mysql",
                                    "readOnly": True
                                },
                                {
                                    "name":"fluentd-config",
                                    "mountPath":"/tmp/fluent.conf",
                                    "subPath":"fluent.conf"
                                }
                            ]
                        }
                    ],
                    "volumes":[
                        {
                            "name":"general-log-config",
                            "configMap":{
                                "name":"mycluster-logs-config",
                                "defaultMode":420,
                                "items":[
                                    {
                                        "key":"general-log.cnf",
                                        "path":"general-log.cnf"
                                    }
                                ]
                            }
                        },
                        {
                            "name":"error-log-config",
                            "configMap":{
                                "name":"mycluster-logs-config",
                                "defaultMode":420,
                                "items":[
                                    {
                                        "key":"error-log.cnf",
                                        "path":"error-log.cnf"
                                    }
                                ]
                            }
                        },
                        {
                            "name":"slow-query-log-config",
                            "configMap":{
                                "name":"mycluster-logs-config",
                                "defaultMode":420,
                                "items":[
                                    {
                                        "key":"slow-query-log.cnf",
                                        "path":"slow-query-log.cnf"
                                    }
                                ]
                            }
                        },
                        {
                            "name":"fluentd-config",
                            "configMap":{
                                "name":"mycluster-fluentd-conf",
                                "defaultMode":420,
                                "items":[
                                    {
                                        "key":"fluent.conf",
                                        "path":"fluent.conf"
                                    }
                                ]
                            }
                        }
                    ]
                }
            }
        }
    }
    get_configmaps = test_obj.get_configmaps_cb()
    cm_prefix = ''
    configmaps = get_configmaps(cm_prefix, logger)
    assert configmaps == logs_spec4_configmaps

    fixture = logs_spec4
    test_obj = logs_spec_factory()
    test_obj.parse(fixture, prefix, logger)
    assert test_obj.collect == True
    with pytest.raises(ApiSpecError, match=rf"{prefix}.{ServerLogType.GENERAL.value}.collect is enabled while {prefix}.{ServerLogType.GENERAL.value}.enabled is not$") as e_info:
        test_obj.validate()

    fixture = logs_spec5
    test_obj = logs_spec_factory()
    test_obj.parse(fixture, prefix, logger)
    assert test_obj.collect == True
    with pytest.raises(ApiSpecError, match=rf"{prefix}.{ServerLogType.SLOW_QUERY.value}.collect is enabled while {prefix}.{ServerLogType.SLOW_QUERY.value}.enabled is not$") as e_info:
        test_obj.validate()


@pytest.fixture
def log_collector_spec_factory() -> Callable[[str, str], LogCollectorSpec]:
    return lambda namespace, cluster : LogCollectorSpec(namespace, cluster)

@pytest.fixture
def log_collector_spec1() -> dict:
    return {
        "image" : "192.168.30.199:5000/fluentd:1.16",
    }

@pytest.fixture
def log_collector_spec2() -> dict:
    return {
        "fluentd" : {}
    }

@pytest.fixture
def log_collector_spec3() -> dict:
    return {
        "fluentd" : {
            "generalLog" : {},
            "errorLog" : {},
            "slowQueryLog": {},
            "recordAugmentation" : {},
            "additionalFilterConfiguration" : ""
        }
    }


def test_log_collector_spec(log_collector_spec_factory: Callable[[str, str], LogCollectorSpec],
                            log_collector_spec1: Dict,
                            log_collector_spec2: Dict,
                            logger: Logger) -> None:
    prefix = "logs.collector"
    ns = "mynamespace"
    cluster = "mycluster"
    test_obj = log_collector_spec_factory(ns, cluster)
    assert test_obj.namespace == ns
    assert test_obj.cluster_name == cluster

    fixture = log_collector_spec1
    test_obj = log_collector_spec_factory(ns, cluster)
    test_obj.parse(fixture, prefix, logger)
    assert test_obj._prefix == prefix
    assert test_obj.image_name == log_collector_spec1["image"]
    assert test_obj.collector is None

    fixture = log_collector_spec2
    test_obj = log_collector_spec_factory(ns, cluster)
    test_obj.parse(fixture, prefix, logger)
    assert test_obj.collector is not None
    assert isinstance(test_obj.collector, FluentdSpec)


@pytest.fixture
def fluentd_spec_factory() -> Callable[[str, str], FluentdSpec]:
    return lambda namespace, cluster : FluentdSpec(namespace, cluster)

@pytest.fixture
def fluentd_spec1() -> dict:
    return {
        "generalLog" : {},
        "errorLog" : {},
        "slowQueryLog": {},
        "recordAugmentation" : {},
        "additionalFilterConfiguration" : ""
    }

@pytest.fixture
def fluentd_spec2() -> dict:
    return {
        "recordAugmentation" : {
            "enabled": False,
        }
    }

@pytest.fixture
def fluentd_spec3() -> dict:
    return {
        "generalLog" : {
            "tag" : "genLogTag",
            "options": {
                "GLoption1": "GLoption1Value",
                "GLoption2": "GLoption2Value"
            }
        },
        "errorLog" : {
            "tag" : "errLogTag",
            "options": {
                "ELoption1": "ELoption1Value",
                "ELoption2": "ELoption2Value"
            }
        },
        "slowQueryLog": {
            "tag" : "genLogTag",
            "options": {
                "SLoption1": "SLoption1Value",
                "SLoption2": "SLoption2Value"
            }
        },
        "recordAugmentation" : {
            "enabled": True,
            "labels": [
                {
                    "fieldName": "pod_name",
                    "labelName": "statefulset.kubernetes.io/pod-name"
                }
            ],
            "annotations" : [
                {
                    "fieldName": "membership-info",
                    "annotationName": "mysql.oracle.com/membership-info"
                }
            ],
            "staticFields": [
                {
                    "fieldName": "static_field_1",
                    "fieldValue": "static_field_1_value"
                }
            ],
            "podFields": [
                {
                    "fieldName": "pod_ip",
                    "fieldPath": "status.podIP"
                },
                {
                    "fieldName": "host_ip",
                    "fieldPath": "status.hostIP"
                }
            ],
            "resourceFields": [
                {
                    "fieldName": "mysql_requests_memory",
                    "containerName": "mysql",
                    "resource": "requests.memory",
                }
            ]
        },
        "additionalFilterConfiguration" : """
filterConfigValueLine1
filterConfigValueLine1
"""
    }


def test_fluentd_spec(fluentd_spec_factory: Callable[[str, str], FluentdSpec],
                      fluentd_spec1: Dict,
                      fluentd_spec2: Dict,
                      fluentd_spec3: Dict) -> None:
    prefix = "logs.collector"
    ns = "mynamespace"
    cluster = "mycluster"

    fixture = fluentd_spec1
    test_obj = fluentd_spec_factory(ns, cluster)
    test_obj.parse(fixture, prefix)

    assert isinstance(test_obj, FluentdSpec)
    assert test_obj.namespace == "mynamespace"
    assert test_obj.cluster_name == "mycluster"

    assert isinstance(test_obj.generalLog, FluentdMysqlLogSpec)
    assert isinstance(test_obj.errorLog, FluentdMysqlLogSpec)
    assert isinstance(test_obj.slowQueryLog, FluentdMysqlLogSpec)
    assert isinstance(test_obj.recordAugmentation, FluentdRecordAugmentationSpec)
    assert isinstance(test_obj.additionalFilterConfiguration, str)

    fixture = fluentd_spec2
    test_obj = fluentd_spec_factory(ns, cluster)
    generalLog_default_tag = test_obj.generalLog.tag
    errorLog_default_tag = test_obj.errorLog.tag
    slowQueryLog_default_tag = test_obj.slowQueryLog.tag
    test_obj.parse(fixture, prefix)
    assert isinstance(test_obj.generalLog, FluentdMysqlLogSpec)
    assert test_obj.generalLog.tag == generalLog_default_tag
    assert test_obj.generalLog.options is not None
    assert test_obj.generalLog.options == {}
    assert isinstance(test_obj.errorLog, FluentdMysqlLogSpec)
    assert test_obj.errorLog.tag == errorLog_default_tag
    assert test_obj.errorLog.options is not None
    assert test_obj.errorLog.options == {}
    assert isinstance(test_obj.slowQueryLog, FluentdMysqlLogSpec)
    assert test_obj.slowQueryLog.tag == slowQueryLog_default_tag
    assert test_obj.slowQueryLog.options is not None
    assert test_obj.slowQueryLog.options == {}
    assert isinstance(test_obj.recordAugmentation, FluentdRecordAugmentationSpec)
    assert test_obj.recordAugmentation.enabled == False
    assert test_obj.recordAugmentation.labels is None
    assert test_obj.recordAugmentation.annotations is None
    assert test_obj.recordAugmentation.staticFields is None
    assert test_obj.recordAugmentation.podFields is None
    assert test_obj.recordAugmentation.resourceFields is None
    assert test_obj.additionalFilterConfiguration is None

    fixture = fluentd_spec3
    test_obj = fluentd_spec_factory(ns, cluster)
    test_obj.parse(fixture, prefix)
    assert isinstance(test_obj.generalLog, FluentdMysqlLogSpec)
    assert test_obj.generalLog.tag == fixture["generalLog"]["tag"]
    assert test_obj.generalLog.options == fixture["generalLog"]["options"]
    assert isinstance(test_obj.errorLog, FluentdMysqlLogSpec)
    assert test_obj.errorLog.tag == fixture["errorLog"]["tag"]
    assert test_obj.errorLog.options == fixture["errorLog"]["options"]
    assert isinstance(test_obj.slowQueryLog, FluentdMysqlLogSpec)
    assert test_obj.slowQueryLog.tag == fixture["slowQueryLog"]["tag"]
    assert test_obj.slowQueryLog.options == fixture["slowQueryLog"]["options"]
    assert isinstance(test_obj.recordAugmentation, FluentdRecordAugmentationSpec)
    assert test_obj.recordAugmentation.enabled == True
    recordAugmentation = fixture["recordAugmentation"]
    assert test_obj.recordAugmentation.labels == recordAugmentation["labels"]
    assert test_obj.recordAugmentation.annotations == recordAugmentation["annotations"]
    assert test_obj.recordAugmentation.staticFields == recordAugmentation["staticFields"]
    assert test_obj.recordAugmentation.podFields == recordAugmentation["podFields"]
    assert test_obj.recordAugmentation.resourceFields == recordAugmentation["resourceFields"]
    assert test_obj.additionalFilterConfiguration == fixture["additionalFilterConfiguration"]
